#include "task_benchmark.hpp"

#include <random>
#include <iostream>
#include <thread>
#include <chrono>
#include <vector>
#include <atomic>

#include <string>

#include "d2_thread_pool.hpp"

struct RandomDurationMaker
{
    RandomDurationMaker()
    {
        std::random_device rd;
        rng.seed(rd());
    }

    void initialize(int64_t minMicroseconds, int64_t maxMicroseconds)
    {
        dist = std::uniform_int_distribution<int64_t>(minMicroseconds, maxMicroseconds);
    }

    std::uniform_int_distribution<int64_t> dist; 
    std::mt19937 rng;

    std::chrono::microseconds operator()() noexcept
    {
        if (dist.max() == dist.min())
        {
            return std::chrono::microseconds(dist.min());
        }

        return std::chrono::microseconds(dist(rng));
    }
};

// 1秒到1000 微秒之间的随机持续时间生成器
RandomDurationMaker randomDuration;

std::atomic<unsigned long long> totalResult = 0;

// 递归计算阶乘
unsigned long long factorial(int n) 
{
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

void Job()
{
    std::chrono::microseconds duration = randomDuration();
    if (duration.count() > 0)
    {
        std::this_thread::sleep_for(duration);
    }
    
    unsigned long long result = factorial(20);
    totalResult += result;
}

void TestThreads(int taskCount) 
{
    std::vector<std::thread> threads;
    threads.reserve(taskCount);

    for (int i=0; i < taskCount; ++i)
    {
        threads.emplace_back(Job);
    }

    for (auto& t : threads)
    {
        t.join();
    }
}

void TestThreadPool(int taskCount) 
{
    d2::ThreadPool pool;

    for (int i=0; i < taskCount; ++i)
    {
        pool.Submit(Job);
    }

    pool.Stop();
}

void TestWithThreadPool(d2::ThreadPool& pool, int taskCount) 
{
    for (int i=0; i < taskCount; ++i)
    {
        pool.Submit(Job);
    }
    
    pool.Stop();
}

void Run(char const* name, std::function<void ()> const& f)
{
    totalResult = 0;

    auto start = std::chrono::high_resolution_clock::now();

    f();

    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();    

    std::cout << name << " - Result: " << totalResult << std::endl;
    std::cout << name << " - Duration: " << duration << " ms" << std::endl;
}

void TestMain(TaskIndex index, int taskCount, int64_t minMicroseconds, int64_t maxMicroseconds)
{
    auto minDuration = std::chrono::microseconds(minMicroseconds);
    auto maxDuration = std::chrono::microseconds(maxMicroseconds);

    randomDuration.initialize(minMicroseconds, maxMicroseconds);

    if (index == TaskIndex::Threads)
    {
        Run("Threads", std::bind(TestThreads, taskCount));
    }
    else if (index == TaskIndex::ThreadPool)
    {
        Run("ThreadPool", std::bind(TestThreadPool, taskCount));
    }
    else if (index == TaskIndex::ExistingThreadPool)
    {
        d2::ThreadPool pool;
        Run("ExistingThreadPool", std::bind(TestWithThreadPool, std::ref(pool), taskCount));
    }

    std::cout << "=========================" << std::endl;
}   